

/// 测试println!宏的使用
/// 
/// # Examples
/// ```
/// let x = 100;
/// println!("x={}",x);
/// ```
/// 
#[test]
fn test00_print() {
    // println!是标准库中定义的宏规则，将格式化后的内容输出到stdout
    // 宏的作用是告诉编译器，将相应的代码进行既定规则的替换(代码展开)
    // rust的宏展开比C语言中的宏更强大，在进行代码替换的同时可以进行类型校验
    // rust的宏展开是导致rust编译慢的一个重要原因
    // 在编译器看来，宏展开不是函数调用，所以用小/中/大括号都可以接受
    // 不过，为了让println!更像函数调用，建议用小括号
    println!("hello world");
    println!["hello world"];
    println!{"hello world"};
    let name = "Liberg";
    let age = 20;
    println!("I'm {}, {} years old.", name, age);
    let v = 10;

    // {}内部可以指定格式化参数的索引
    println!("{0}*{0}={1}", v, v*v);
    // 输出{}，需要转义
    println!("{{}}");
    // 输出{100}
    println!("{{{}}}", 100);
    
    // p是v的借用（引用）
    let p = &v;
    // {:p}的格式可以打印引用本身在内存中的地址
    println!("addr of v: {:p}", p);
    // *p取得引用p所指向的值
    println!("v = {}", *p);// 10
    // &p是一个新的引用，这个引用指向的是栈内存中的p
    println!("addr of &p: {:p}", &p);
    println!("v = {}", **(&p));// 10
    // 输出size of p: 4，引用占4个字节
    println!("size of p: {}", std::mem::size_of_val(p));

    // 输出到stderr
    eprintln!("some error happened");

    let x = 100;
    // 会打印出调试信息：文件名+行号+表达式的值
    // [xxx.rs:30] x > 50 = true
    dbg!(x > 50);

    // macro_rules!是定义宏规则的关键字
    macro_rules! my_max {
        // $x和$y用于指代要匹配的输入
        // 紧接着冒号后面是输入的类型，常见的有：
        // tt 匹配一个token
		// ty 匹配一个类型，比如i32、Vec列表类型等
        // expr 匹配一个表达式
        // stmt 匹配一个语句，比如赋值语句、return语句。
        // block 匹配一个代码块{...}
        // item 匹配函数、结构体、模块等
        ($x:tt, $y:tt)=>{
            if $x > $y {$x} else {$y}
        }
    }
    println!("max is {}", my_max!(3,4));
    println!("max is {}", my_max!(30usize,40usize));
    // 上面的例子将 `if x>y {x} else {y}`这样的表达式
    // 包装成了一个宏

    // 还可以尝试一下将 `10 as i16`这样的代码
    // 通过抽取前面的数值和as后面的类型作为宏参数
    // 包装一个宏出来。 
    macro_rules! my_as {
        ($v:tt, $t:ty)=>{
            $v as $t
        }
    }
    let x = my_as!(10, i16);
    println!("x={}", x);
    //因为是i16类型，所以占用2个字节
    println!("size of x={}", std::mem::size_of_val(&x));
}

#[test]
#[allow(dead_code)]
#[allow(unused_variables)]
fn test01_basic_types() {
    let _b: bool = true;
    let i1:i8 = 1;
    //rust允许出现过的变量名被重新定义为新的类型
    let i1:i16 = 1;
    let i1:i32 = 1;
    let i1:i64 = 1;
    let i1:i128 = 1;
    
    // u8类型本质就是1个字节
    let i2:u8 = 0xFF;
    let i2:u8 = b'A';//可以用b后跟字符进行初始化
    let i2:u16 = 0xFF;//16进制
    let i2:u32 = 0o77;//8进制
    let i2:u64 = 0b1111_1111;//2进制
    let i2:u128 = 100_000;//默认是10进制

    //isize/usize占用的字节取决于处理器位数
    //数组和列表的下标就是usize类型
    //16位机器上，占2个字节
    //32位机器上，占4个字节
    //64位机器上，占8个字节
    let i1:isize = 1;
    let i2:usize = 100;
    // `i2 as i32`是把usize类型的i2转成i32类型
    let end: i32 = i2 as i32 - 1;

    // bool类型占用1个字节
    println!("{}", std::mem::size_of::<bool>());//1
    // i8类型占用1个字节
    println!("{}", std::mem::size_of_val(&100i8));//1
    //rust中char类型占用4个字节
    let c:char = 'x';
    println!("{}", std::mem::size_of_val(&c));//4
    
    // ()类型代表“空”（类比其他语言中的null/nil），占用0个字节
    let empty = ();
    // 等号右边是一个语句(Statment）块，语句块的
    // 最后一个表达式(Expression)的值就是该语句块的值
    let i_1 = {1i32};//i32类型的1
    let empty = {};//语句块的值为()
    println!("empty is: {:?}", empty);//empty is: ()
    println!("{}", std::mem::size_of_val(&empty));//0

    //元组，tuple
    let pp: (String, char, i32) = ("张三".to_string(), 'M', 20);
    println!("{:?}", pp);
    // 通过type关键字，给元组定义一个别名Person
    // 当然，逻辑上更好的做法是定义为一个struct
    type Person = (String, char, i32);
    let p: Person = pp;
    println!("name {}, gender: {}, age: {}", p.0, p.1, p.2);

    println!("-------------------");
    assert_eq!(100.max(200), 200);
    assert_eq!(100.cmp(&1000), std::cmp::Ordering::Less);
    assert_eq!((-10i32).abs(), 10);
    
    use std::ops::Add;
    assert_eq!((-10i32).add(10), 0);
    assert!(100.ge(&80));//true, ge是great equal的简写
    assert!(100 >= 80);//true

    //求得整数二进制形式中1的个数
    assert_eq!(5i32.count_ones(), 2);
    //浮点类型才可以sqrt()
    assert_eq!(100f32.sqrt(),10f32);
}

#[test]
fn test02_array() {
    //整型数组
    let nums1 = [1, 2, 3, 4, 5, 6];
    let nums2:[i32; 6] = [1,2, 3, 4, 5, 6];

    // 初始化为6个8
    let nums3 = [8; 6];
    // 由于数组没有实现Display trait(特性、接口)
    // 但数组实现了Debug trait，可以用{:?}进行格式化
    println!("nums1: {:?}", nums1);
    println!("nums2: {:?}", nums2);
    println!("nums3: {:?}", nums3);
    //遍历数组的几种方式
    for i in 0..nums1.len() {
        println!("{} ", nums1[i]);
    }
    for (pos, v) in nums1.iter().enumerate() {
        print!("nums[{}]={}", pos, v);
    }
    println!();
    for num in nums1 {
        print!("{} ", num);
    }
    println!();
    //通过iter()迭代器拿到的是元素的引用
    //也可以显示通过*解应用得到原数值
    for num in nums1.iter() {
        print!("{} ", num);
        print!("{} ", *num);
    }
    println!();
    // for-in语句中支持直接遍历数组的引用
    for i in &nums2 {
        print!("{} ", i);
        print!("{} ", *i);
    }
    println!();

    // 数组切片，
    // 切片是对原数据的“部分引用”，需要&符号
    // 数据的切片之于原数据，就好比数据库中的视图之于数据表
    let full_slice: &[i32] = &nums1[..];
    println!("{:?}", full_slice);// 完整切片
    // 对切片类型，还可以进行切片
    println!("{:?}", &full_slice[1..3]);// [2,3]
    println!("{:?}", &&nums1[0..4][1..3]);// [2,3]
    println!("{:?}", &nums1[0..2]);// [1,2]
    println!("{:?}", &nums1[2..]);// [3,4,5,6]
    println!("{:?}", &nums1[1..nums1.len()-1]);// [2,3,4,5]

    //b开头的字符串，在编译器看来就是u8数组的引用
    let x: &[u8; 11] = b"Hello World";
    for c in x {
        // c是&u8类型，即u8的引用类型
        print!("{} ", *c as char);
    }
    println!();
    // u8数组的切片
    let x_slice1 = &x[0..2];
    println!("{:?}", x_slice1);//[72, 101]

    //数组排序，mut关键字让数组中的元素可以被修改
    let mut data: [i32; 7] = [-4,3,2,1,9,8,10];
    data[0] = -20;
    let mut sorted1 = data.clone();
    sorted1.sort();
    //默认从小到大排序
    println!("sorted: {:?}", sorted1);

    // 切片类型排序：必须是mut切片才能进行排序
    let sorted2: &mut [i32] = &mut data.clone()[..];
    // sort_unstable()不保证排序的稳定性，
    // 但性能更好，并且不会有额外的内存分配
    sorted2.sort_unstable();
    println!("origin: {:?}", data);
    println!("sorted: {:?}", sorted2);

    // 闭包是函数式编程中的一个概念。
    // 可以简单将闭包简单理解为编译后会存在于可执行文件代码段中的一个结构。
    // 结构中包含一个可以调用的函数，以及可能被需要的额外数据。
    let print = ||{ println!("Hello") };
    let add = |x: i32, y: i32|{ x+y };
    print();
    add(1,2);

    // 倒排（从大到小）
    let mut sorted3 = data.clone();
    sorted3.sort_by(|x,y|y.cmp(x));
    println!("{:?}", sorted3);

    //按原值的绝对值进行排序
    let mut sorted4 = data.clone();
    sorted4.sort_by_key(|x|x.abs());
    println!("{:?}", sorted4);

    // 数组支持的一些其他常用方法
    let (part1, part2) = [1,2,3,4,5].split_at(2);
    println!("part1={:?}", part1);//[1,2]
    println!("part2={:?}", part2);//[3,4,5]
    let mut arr = [1,2,3,4];
    arr.swap(1,2);
    println!("arr: {:?}", arr);//[1, 3, 2, 4]
    arr.reverse();
    println!("arr: {:?}", arr);//[4, 2, 3, 1]
    //二分查找
    let index: Result<usize, usize> = [1,2,3,4,5].binary_search(&4);
    //index: Ok(3)
    println!("index: {:?}", index);
    let last_elem: Option<&i32> = [1,2,3,4,5].last();
    //last elem: Some(5)
    println!("last elem: {:?}", last_elem);
}

/// Rust中Vec结构是对列表的实现，类比java中的ArrayList
#[test]
#[allow(unused_variables)]
fn test03_list() {
    // 空列表，初始容量为0
    let mut list: Vec<i32> = Vec::new();
    println!("capacity: {}", list.capacity());//0
    // 首次添加元素，容量扩为4
    list.push(1);
    list.push(2);
    list.push(3);
    list.push(4);
    println!("capacity: {}", list.capacity());//4
    // 元素超出容量后，容量扩大一倍
    list.push(5);
    println!("capacity: {}", list.capacity());//8
    println!("list[0]={}", list[0]);//1
    println!("{:?}", list);//[1, 2]
    // 加换行和缩进的“美化”打印
    // [
    //     1,
    //     2,
    // ]
    println!("{:#?}", list);
    // 创建一个带初始容量的列表
    let list: Vec<i32> = std::vec::Vec::with_capacity(16);
    println!("capacity: {}", list.capacity());//16


    // 更常用的是用vec!宏用来创建和初始化一个Vec
    let digits: Vec<i32> = vec![0, 1, 2, 3, 4, 5];
    let nums = vec![0; 100];
    //nums: len=100, capacity=100
    println!("nums: len={}, capacity={}", nums.len(), nums.capacity());

    let digits:Vec<i32> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    for i in 0..digits.len() {
        println!("digits[{}]={}", i, digits[i]);
    }
    
    // 让idx可修改，需要加mut(代表mutable)关键字
    let mut idx = 0;
    for x in digits.iter() {
        println!("digits[{}]={}", idx, x);
        // 和Python一样，rust不支持++和--
        idx += 1;
    }

    println!("sum={}", digits.iter().sum::<i32>());//45
    println!("sum={}", digits.iter().fold(0, |x,a|x+a));//45
    
    for (pos, v) in digits.iter().enumerate() {
        println!("digits[{}]={}", pos, v);
    }

    let mut sum = 0;
    for x in digits {
        sum += x;
    }
    println!("sum={}", sum);//45
    

    //列表切片
    let digits = vec![0,1,2,3,4,5];
    println!("{:?}", &digits[0..2]);//[0,1]

    // 列表排序
    let disorder = vec![3,-1,1,2,4,0];
    // 从大到小排序，前面一小节讲述过闭包
    let mut sorted1 = disorder.clone();
    sorted1.sort_by(|x,y|y.cmp(x));
    println!("{:?}", sorted1);
    
    // 通过迭代器进行数据过滤和处理，然后collect()收集起来
    let some_data = vec![1,2,3,4,5,6];
    let result_data: Vec<i32> = some_data.iter()
            .filter(|&x|x%2==0)
            .map(|x|x*x)
            .collect();
    println!("result_data: {:?}", result_data);//[4,16,36]

    let found = [1, 2, 3].binary_search(&100);
    //Err(3)
    println!("{:?}", found);
    let last_elem: Option<&i32> = sorted1.last();
    //Some(-1)
    println!("{:?}", last_elem);
}

/// enum枚举 + match模式匹配 + Option<T> + Result<T,E>
#[test]
#[allow(unused_variables)]
fn test04_enum_match() {
    // 定义一个枚举类型
    enum Gender {
        Male = 1,
        Female = 2,
    }

    //枚举类型占用的字节数，取决于其中占用字节数最大的成员
    // 如果修改Gender定义中Female=2000，那么Gender
    // 类型将会占用2个字节
    println!("size: {}", std::mem::size_of::<Gender>());//1
    
    //模式匹配
    fn gender_str(g: Gender) -> String {
        // match是rust中用于模式匹配的关键字
        match g {
            Gender::Male => "Male".to_owned(),
            Gender::Female => "Female".to_owned(),
        }
    }
    println!("{:?}", gender_str(Gender::Male));//"Male"
    println!("{:?}", gender_str(Gender::Female));//"Female"

    //认识Option<T>
    let x: Option<i32> = None;
    let y: Option<i32> = Some(100);
    println!("{:?}", x);//None
    println!("{:?}", y);//Some(100)
    // 也是模式匹配
    if let Some(xval) = y {
        println!("Some(x={})", xval);//Some(x=100)
    }
    //unwrap和expect内部通过match将Option中的值取出来，
    //如果没有值，会panic!
    //expect支持传入自定义的panic!信息
    let v = y.unwrap();
    // let v2 = x.expect("Some error happened");


    enum MyOption<T> {
        None,
        Some(T),
    }
    let x: MyOption<i32> = MyOption::None;
    let x = MyOption::Some(100);

    // 实现了Debug trait，现在可以被打印了
    println!("{:?}", x);
    println!("{:?}", y);
    use std::fmt::Debug;
    // 要求泛型参数T也必须实现了Debug
    impl<T: Debug> Debug for MyOption<T>
    {
        // 实现Debug trait中的fmt方法
        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
            match self {
                // 往f中写入"None"
                Self::None => write!(f, "None"),
                // 带参数的模式匹配
                Self::Some(arg) => f.debug_tuple("Some").field(arg).finish(),
            }
        }
    }

    // 1.55版本新增的open range pattern
    let score: usize = 60;
    let score_desc:&str = match score {
        0..=59 => "D",
        60..=74 => "C",
        75..=89 => "B",
        90..=100 => "A",
        _ => unreachable!(),
    };
    println!("score: {}, score_desc:{}", score, score_desc);

    
    //认识Resut<T,E>
    let x: Result<i32, i32> = Ok(0);
    let y: Result<i32, i32> = Err(99);
    println!("{:?}", x);
    println!("{:?}", y);

    let x1 = x.unwrap();
    // let y1 = y.expect("Is Err");
}

#[test]
#[allow(unused_variables)]
#[allow(unused_assignments)]
fn test05_ownership() {

    let mut x = 1;
    let y = 10;
    // i32类型实现了Copy trait
    // 这里的赋值，是复制行为
    x = y;
    // y的所有权没有发生转移
    println!("y={}", y);//10
    println!("x={}", x);//10
    // 打印地址，发现y和x地址不同
    // 但两个地址上存放的值都是10
    println!("{:p}", &y);//0xc4236fee5c
    println!("{:p}", &x);//0xc4236fee58


    //cannot borrow `x` as mutable more than once at a time
    //cannot borrow `x` as mutable more than once at a time
    // let mut x = "Hello".to_string();
    // let mut x_b2 = &mut x;
    // let mut x_b3 = &mut x;
    // x_b2.push_str("World");
    // println!("{:?}", x_b0);
    // println!("{:?}", x_b1);

    // let p: Point = Point {x:1, y:1, };
    // // q是对p所指内容的借用
    // let mut q: &Point = &p;
    // // 所有权仍然归p
    // println!("p.x={}", p.x);
    // println!("q.x={}", q.x);
    // {
    //     let r = Point{x:10, y:10, };
    //     // q = &r; 
    // }
    // println!("q.x={}", q.x);
}